using System;
using System.Collections.Generic;
using System.Linq;
using Db4objects.Db4o.Linq;
using SharedObjects;
using TheGame.Server;
using TheGameLib;

namespace TheGame.Webservices
{
	public partial class TheGameService
	{
		#region Email
		public bool CreateEmailAccount(Guid sessionId, ulong systemId, string accountName)
		{
			Agent me;
			if (!CheckIsValidAgent(sessionId, out me))
			{
				SendServerError(ServerErrorSeverity.Error, "Invalid session id.");
				return false;
			}
			AgentSessionObject aso = World.Instance.GetAgentSession(sessionId);
			AgentConnection conn = aso.GetAgentConnectionByTargetSystem(systemId);
			if (conn == null)
			{
				SendServerError(ServerErrorSeverity.Error, "No connection exists to target system.");
				return false;
			}

			if (me.EmailAccounts == null)
				me.EmailAccounts = new List<EmailAccount>();

			//we dont permit more than 1 account per CS per agent.
			if (me.EmailAccounts.Where(it => it.SystemId == systemId).Count() > 0)
			{
				SendServerError(ServerErrorSeverity.Error, "You already have an email account on this system.");
				return false;
			}
			ComputerSystem cs = (from ComputerSystem c in Db where c.Id == systemId select c).FirstOrDefault();
			if (cs == null)
				return false;

			cs.Log(Db, conn.GetConnectedHostId(systemId), LoggingAction.FileCreated, "Email account " + accountName);
			EmailAccount account = new EmailAccount
			{
				Id = Guid.NewGuid().ToLong(),
				Emails = new List<EmailHeader>(),
				Name = accountName,
				SystemId = systemId
			};
			me.EmailAccounts.Add(account);
			Db.PersistEntity(me);
			Db.Commit();
			return true;
		}

		public bool SendEmail(Guid sessionId, ulong systemId, ulong accountId, EmailHeader header, EmailBody body)
		{
			Agent me;
			if (!CheckIsValidAgent(sessionId, out me))
			{
				SendServerError(ServerErrorSeverity.Error, "Invalid session id.");
				return false;
			}
			AgentSessionObject aso = World.Instance.GetAgentSession(sessionId);
			if (aso == null)
			{
				SendServerError(ServerErrorSeverity.Error, "No session object found for session id.");
				return false;
			}

			AgentConnection conn = aso.GetAgentConnectionByTargetSystem(systemId);
			if (conn == null)
			{
				SendServerError(ServerErrorSeverity.Error, "No connection to target system exists.");
				return false;
			}

			//1. Check if sender has a valid email account, and fetch that account, since we also store the email in the Sent folder.
			EmailAccount sender;
			if (conn.IsHacked)
				sender = (from EmailAccount a in Db where a.Id == accountId && a.SystemId == systemId select a).FirstOrDefault();
			else
				sender = (from Agent agt in Db where agt.Id == me.Id from EmailAccount ea in agt.EmailAccounts where ea.Id == accountId && ea.SystemId == conn.TargetSystemId select ea).FirstOrDefault();

			if (sender == null)
			{
				SendServerError(ServerErrorSeverity.Error, "Sender does not exist.");
				return false;
			}
			ComputerSystem cs = (from ComputerSystem c in Db where c.Id == systemId select c).FirstOrDefault();
			if (cs == null)
			{
				SendServerError(ServerErrorSeverity.Error, "Target system not found");
				return false;
			}

			bool hadErrors = false;
			header.Id = Guid.NewGuid().ToLong();
			body.Id = Guid.NewGuid().ToLong();
			header.EmailBodyId = body.Id;
			header.Folder = EmailFolder.Sent;
			header.From = string.Format("{0}@{1}.{2}.{3}", sender.Name, cs.Name.Replace(" ", "").ToLower(), cs.Corporation.Name.Replace(" ", "").ToLower(), cs.Corporation.TopLevelDomain.Name.ToLower());
			header.CreationTimestamp = World.Instance.WorldTime;
			sender.Emails.Add(header);

			Db.PersistEntity(body);
			Db.PersistEntity(sender);

			foreach (string to in header.Recipients)
			{
				EmailAccount account;
				if (!GetEmailAccountByAddress(to, out account))
					hadErrors = true;
				else
					if (!StoreNewEmailInAccount(account, header, body, to))
						hadErrors = true;
			}

			foreach (string to in header.CCs)
			{
				EmailAccount account;
				if (!GetEmailAccountByAddress(to, out account))
					hadErrors = true;
				else
					if (!StoreNewEmailInAccount(account, header, body, to))
						hadErrors = true;
			}

			foreach (string to in header.BCCs)
			{
				EmailAccount account;
				if (!GetEmailAccountByAddress(to, out account))
					hadErrors = true;
				else
					if (!StoreNewEmailInAccount(account, header, body, to))
						hadErrors = true;
			}

			if (hadErrors)
				Db.Rollback();
			else
				Db.Commit();
			if (hadErrors)
			{
				SendServerError(ServerErrorSeverity.Error, "Could not send mail because at least one of the recipients was not found.");
			}
			return !hadErrors;
		}

		public List<EmailHeader> GetEmailsForFolder(Guid sessionId, ulong systemId, ulong accountId, EmailFolder folder)
		{
			Agent me;
			if (!CheckIsValidAgent(sessionId, out me))
			{
				SendServerError(ServerErrorSeverity.Error, "Invalid session id.");
				return null;
			}

			AgentSessionObject aso = World.Instance.GetAgentSession(sessionId);
			if (aso == null)
			{
				SendServerError(ServerErrorSeverity.Error, "Session object not found for session id.");
				return null;
			}

			AgentConnection conn = aso.GetAgentConnectionByTargetSystem(systemId);
			if (conn == null)
			{
				SendServerError(ServerErrorSeverity.Error, "No connection to target system exists.");
				return null;
			}


			EmailAccount account = (from EmailAccount acct in aso.AgentInDb.EmailAccounts where acct.Id == accountId && acct.SystemId == systemId select acct).FirstOrDefault();
			if (account == null) //if this is not the user's own account
			{
				if (!conn.IsHacked) //and the user did not hack the machine
				{
					SendServerError(ServerErrorSeverity.Error, "Cannot access this mail account. It's not your's and you didn't hack into this system.");
					return null;
				}
				
				//user is connected to this machine and has hacked in, he can access all user's accounts.
				account = (from EmailAccount acct in Db where acct.Id == accountId && acct.SystemId == systemId select acct).FirstOrDefault();
				if (account == null)
				{
					SendServerError(ServerErrorSeverity.Error, "Specified account does not exist.");
					return null;
				}
			}
            return account.Emails != null ? account.Emails.Where(it => !it.IsDeleted && it.Folder == folder).OrderByDescending(it => it.CreationTimestamp).ToList() : null;
		}

		public EmailBody GetEmailBody(Guid sessionId, ulong systemId, ulong accountId, ulong headerId)
		{
			Agent me;
			if (!CheckIsValidAgent(sessionId, out me))
			{
				SendServerError(ServerErrorSeverity.Error, "Invalid session id.");
				return null;
			}

			AgentSessionObject aso = World.Instance.GetAgentSession(sessionId);
			if (aso == null)
			{
				SendServerError(ServerErrorSeverity.Error, "No session object found for session id.");
				return null;
			}

			AgentConnection conn = aso.GetAgentConnectionByTargetSystem(systemId);
			if (conn == null)
			{
				SendServerError(ServerErrorSeverity.Error, "No connection found to target system.");
				return null;
			}


			EmailAccount account = (from acct in me.EmailAccounts where acct.Id == accountId && acct.SystemId == systemId select acct).FirstOrDefault();
			if (account == null) //if this is not the user's own account
			{
				if (!conn.IsHacked) //and the user did not hack the machine
				{
					SendServerError(ServerErrorSeverity.Error, "Cannot access this account without hacking this machine.");
					return null;
				}
				//user is connected to this machine and has hacked in, he can access all user's accounts.
				account = (from EmailAccount acct in Db where acct.Id == accountId && acct.SystemId == systemId select acct).FirstOrDefault();
				if (account == null)
				{
					SendServerError(ServerErrorSeverity.Error, "Specified email account does not exist.");
					return null;
				}
			}
			ulong bodyId = (from EmailHeader hdr in account.Emails where hdr.Id == headerId select hdr.EmailBodyId).FirstOrDefault();
			if (bodyId == 0)
			{
				SendServerError(ServerErrorSeverity.Error, "Specified email header has no body.");
				return null;
			}
			EmailBody body = (from EmailBody bdy in Db where bdy.Id == bodyId select bdy).FirstOrDefault();
			return body;
		}

		public List<ShortEmailAccountInfo> GetEmailAccountsForSystem(Guid sessionId, ulong systemId)
		{
			Agent me;
			if (!CheckIsValidAgent(sessionId, out me))
			{
				SendServerError(ServerErrorSeverity.Error, "Invalid session id.");
				return null;
			}

			AgentSessionObject aso = World.Instance.GetAgentSession(sessionId);
			if (aso == null)
			{
				SendServerError(ServerErrorSeverity.Error, "No session object found for session id.");
				return null;
			}

			AgentConnection conn = aso.GetAgentConnectionByTargetSystem(systemId);
			if (conn == null)
			{
				SendServerError(ServerErrorSeverity.Error, "No connection to target system exists.");
				return null;
			}

			ComputerSystem cs = (from ComputerSystem c in Db where c.Id == systemId select c).FirstOrDefault();
			if (cs == null)
				return null;
			List<ShortEmailAccountInfo> retval;
			if ((conn.IsHacked)) //admin users do not have to hack to have this functionality || CheckIsValidAgent(sessionId, true) || 
			{
				retval = new List<ShortEmailAccountInfo> { null };
				retval.AddRange((from EmailAccount acct in Db
								 where acct.SystemId == systemId && !acct.IsDeleted
								 select new ShortEmailAccountInfo
								 {
									 Id = acct.Id,
									 Name = acct.Name
								 }));
				cs.Log(Db, conn.GetConnectedHostId(conn.TargetSystemId), LoggingAction.HackEmailAccounts, null);
				return retval;
			}
			ShortEmailAccountInfo account = (from Agent a in Db where a.Id == me.Id from EmailAccount acct in a.EmailAccounts where acct.SystemId == systemId && !acct.IsDeleted select new ShortEmailAccountInfo { Id = acct.Id, Name = acct.Name }).FirstOrDefault();
			if (account == null)
				return null;

			return new List<ShortEmailAccountInfo> { account };
		}

		public bool MoveEmail(Guid sessionId, ulong systemId, ulong accountId, ulong emailHeaderId, EmailFolder folder)
		{
			Agent me;
			if (!CheckIsValidAgent(sessionId, out me))
			{
				SendServerError(ServerErrorSeverity.Error, "Invalid session id.");
				return false;
			}

			AgentSessionObject aso = World.Instance.GetAgentSession(sessionId);
			if (aso == null)
			{
				SendServerError(ServerErrorSeverity.Error, "No session object found for session id.");
				return false;
			}

			AgentConnection conn = aso.GetAgentConnectionByTargetSystem(systemId);
			if (conn == null)
			{
				SendServerError(ServerErrorSeverity.Error, "No connection to target system exists.");
				return false;
			}


			EmailAccount account = (from acct in me.EmailAccounts where acct.Id == accountId && acct.SystemId == systemId select acct).FirstOrDefault();
			if (account == null) //if this is not the user's own account
			{
				if (!conn.IsHacked) //and the user did not hack the machine
				{
					SendServerError(ServerErrorSeverity.Error, "Cannot access this email account without hacking into this system.");
					return false;
				}
				//user is connected to this machine and has hacked in, he can access all user's accounts.
				account = (from EmailAccount acct in Db where acct.Id == accountId && acct.SystemId == systemId select acct).FirstOrDefault();
				if (account == null)
				{
					SendServerError(ServerErrorSeverity.Error, "Specified email account does not exist.");
					return false;
				}
			}
			EmailHeader header = (from EmailHeader h in account.Emails where h.Id == emailHeaderId select h).FirstOrDefault();
			if (header == null)
			{
				SendServerError(ServerErrorSeverity.Error, "Specified email does not exist.");
				return false;
			}
			header.Folder = folder;
			Db.PersistEntity(header);
			Db.Commit();
			return true;
		}

		public bool DeleteEmail(Guid sessionId, ulong systemId, ulong accountId, ulong emailHeaderId)
		{
			Agent me;
			if (!CheckIsValidAgent(sessionId, out me))
			{
				SendServerError(ServerErrorSeverity.Error, "Invalid session id.");
				return false;
			}

			AgentSessionObject aso = World.Instance.GetAgentSession(sessionId);
			if (aso == null)
			{
				SendServerError(ServerErrorSeverity.Error, "No session object found for session id.");
				return false;
			}

			AgentConnection conn = aso.GetAgentConnectionByTargetSystem(systemId);
			if (conn == null)
			{
				SendServerError(ServerErrorSeverity.Error, "No connection to target system exists.");
				return false;
			}

			EmailAccount account = (from acct in me.EmailAccounts where acct.Id == accountId && acct.SystemId == systemId select acct).FirstOrDefault();
			if (account == null) //if this is not the user's own account
			{
				if (!conn.IsHacked) //and the user did not hack the machine
				{
					SendServerError(ServerErrorSeverity.Error, "Cannot access this email account without hacking into this system.");
					return false;
				}
				//user is connected to this machine and has hacked in, he can access all user's accounts.
				account = (from EmailAccount acct in Db where acct.Id == accountId && acct.SystemId == systemId select acct).FirstOrDefault();
				if (account == null)
				{
					SendServerError(ServerErrorSeverity.Error, "Specified email account does not exist.");
					return false;
				}
			}

			EmailHeader header = (from EmailHeader h in account.Emails where h.Id == emailHeaderId select h).FirstOrDefault();
			if (header == null)
			{
				SendServerError(ServerErrorSeverity.Error, "Specified email does not exist.");
				return false;
			}
			if (header.Folder != EmailFolder.Trash)
				header.Folder = EmailFolder.Trash;
			else
			{
				header.IsDeleted = true;
				header.DeletionTimeStamp = DateTime.Now;
			}
			ComputerSystem cs = (from ComputerSystem c in Db where c.Id == systemId select c).FirstOrDefault();
			if (cs == null)
				return false;
			cs.Log(Db, conn.GetConnectedHostId(systemId), LoggingAction.FileDeleted, "Email deleted on " + account.Name);
			Db.PersistEntity(header);
			Db.Commit();
			return true;
		}

		public bool MarkEmailAsSpam(Guid sessionId, ulong systemId, ulong accountId, ulong emailHeaderId)
		{
			Agent me;
			if (!CheckIsValidAgent(sessionId, out me))
				return false;

			AgentSessionObject aso = World.Instance.GetAgentSession(sessionId);
			if (aso == null)
				return false;

			AgentConnection conn = aso.GetAgentConnectionByTargetSystem(systemId);
			if (conn == null)
				return false;


			EmailAccount account = (from acct in me.EmailAccounts where acct.Id == accountId && acct.SystemId == systemId select acct).FirstOrDefault();
			if (account == null) //if this is not the user's own account
			{
				if (!conn.IsHacked) //and the user did not hack the machine
					return false;
				//user is connected to this machine and has hacked in, he can access all user's accounts.
				account = (from EmailAccount acct in Db where acct.Id == accountId && acct.SystemId == systemId select acct).FirstOrDefault();
				if (account == null)
					return false;
			}

			EmailHeader header = (from EmailHeader h in account.Emails where h.Id == emailHeaderId select h).FirstOrDefault();
			if (header == null)
				return false;
			header.Folder = EmailFolder.Spam;
			Db.PersistEntity(header);
			Db.Commit();
			return true;
		}

		public string GetHostnameAddress(Guid sessionId, ulong systemId)
		{
			if (!CheckIsValidAgent(sessionId))
			{
				SendServerError(ServerErrorSeverity.Error, "Invalid session id.");
				return null;
			}
			ComputerSystem system = (from ComputerSystem cs in Db where cs.Id == systemId select cs).FirstOrDefault();
			if (system != null)
				return system.GetHostname();
			SendServerError(ServerErrorSeverity.Error, "Specified system does not exist.");
			return null;
		}
		#endregion
	}
}